package org.hzero.interfaces.common.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.hzero.interfaces.common.json.KnifeDateTimeDeserializer;
import org.hzero.interfaces.common.json.KnifeDateTimeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 缓存下来需要被日志监控方法的参数Class及对应是否会被忽略的标记
 *
 * @author: tianhao.luo@hand-china.com 2021/7/2  上午11:46
 */
public class KnifeLogParamUtil {
    public static final Logger LOGGER = LoggerFactory.getLogger(KnifeLogParamUtil.class);
    //
    private static final ReentrantReadWriteLock REENTRANT_READ_WRITE_LOCK = new ReentrantReadWriteLock();
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    /**
     * 存类的flag
     */
    private static final ThreadLocal<AtomicBoolean> FLAG_THREAD_LOCAL = ThreadLocal.withInitial(() -> new AtomicBoolean(false));
    @SuppressWarnings(value = "unchecked")
    private static volatile Map<Class<?>, Boolean> map = new HashMap(512 << 1);

    static {
        // 将Date类型的属性，如果没有添加JSONFormat注解的，默认按照"yyyy-MM-dd HH:mm:ss"进行序列化
        SimpleModule module = new SimpleModule();
        module.addSerializer(Date.class, new KnifeDateTimeSerializer());
        module.addDeserializer(Date.class, new KnifeDateTimeDeserializer());
        OBJECT_MAPPER.registerModule(module);
    }

    /**
     * 判断参数的Class是否在ignoreClassArray中,通过缓存
     *
     * @param objClass         参数的Class
     * @param ignoreClassArray 忽略列表
     * @return 被忽略则返回true;否则返回false
     */
    public static boolean argIsIgnore(Class<?> objClass, Class<?>... ignoreClassArray) {
        Boolean aBoolean = argIsIgnoreByCache(objClass);
        if (aBoolean == null) {
            ReentrantReadWriteLock.WriteLock writeLock = REENTRANT_READ_WRITE_LOCK.writeLock();
            writeLock.lock();
            try {
                // 多线程阻塞进入时，二次判断是否已经被初始化过了
                aBoolean = argIsIgnoreByCache(objClass);
                if (aBoolean == null) {
                    dfs(objClass, ignoreClassArray);
                    AtomicBoolean flag = FLAG_THREAD_LOCAL.get();
                    boolean tempFlag = flag.getAndSet(false);
                    map.put(objClass, tempFlag);
                    return tempFlag;
                } else {
                    return aBoolean;
                }
            } finally {
                writeLock.unlock();
                FLAG_THREAD_LOCAL.remove();
            }
        } else {
            return aBoolean;
        }
    }

    /**
     * 从缓存中获取
     *
     * @param objClass 参数的类型
     * @return 有就返回对应值，没有返回的是null
     */
    private static Boolean argIsIgnoreByCache(Class<?> objClass) {
        ReentrantReadWriteLock.ReadLock readLock = REENTRANT_READ_WRITE_LOCK.readLock();
        readLock.lock();
        try {
            return map.get(objClass);
        } finally {
            readLock.unlock();
        }
    }


    /**
     * 判断参数的Class是否在ignoreClassArray中
     * <p>
     * 被忽略则设置flag为true
     *
     * @param objClass         参数的Class
     * @param ignoreClassArray 忽略列表
     */
    private static void dfs(Class<?> objClass, Class<?>... ignoreClassArray) {
        if (ignoreClassArray != null && ignoreClassArray.length > 0) {
            if (objClass == null || FLAG_THREAD_LOCAL.get().get()) {
                return;
            }
            // 1.判断当前类型的class是否在ignoreClassList中;
            // 从底层往上找，最初的objClass是被忽略的类的继承或实现，则表示它也要被忽略
            for (Class<?> aClass : ignoreClassArray) {
                if (objClass.getName().equals(aClass.getName())) {
                    FLAG_THREAD_LOCAL.get().set(true);
                    return;
                }
            }

            Class<?>[] interfaces = objClass.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                // 将递归过程中的类也存储下来
                dfs(anInterface, ignoreClassArray);
            }

            // 2.不在其中就去父类中找
            dfs(objClass.getSuperclass(), ignoreClassArray);
        }
    }

    /**
     * 通过JackSon进行深拷贝，避免参数在方法中做了修改
     *
     * @param args             参数数组
     * @param ignoreClassArray 不做序列化记录的参数类型
     * @return 记录的参数数组
     */
    public static Object[] deepCloneArgs(Object[] args, Class<?>... ignoreClassArray) {
        List<Object> objects = new ArrayList<>(args.length);
        for (Object arg : args) {
            if (!argIsIgnore(arg.getClass(), ignoreClassArray)) {
                try {
                    String s = OBJECT_MAPPER.writeValueAsString(arg);
                    Object o = OBJECT_MAPPER.readValue(s, arg.getClass());
                    objects.add(o);
                } catch (IOException e) {
                    LOGGER.error("序列化参数出错：" + e.getMessage());
                }
            }
        }
        return objects.toArray();
    }


    /**
     * 通过JackSon进行深拷贝，避免参数在方法中做了修改
     * 同时将参数转化为map，能够在aop记录入站请求时，记录路径参数
     *
     * @param args             参数数组
     * @param parameters       参数名称
     * @param ignoreClassArray 不做序列化记录的参数类型
     * @return 记录的参数map
     */
    public static Map<String, Object> deepCloneArgs(Object[] args, Parameter[] parameters, Class<?>... ignoreClassArray) {
        Map<String, Object> map = new HashMap<>(args.length);
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg != null && !argIsIgnore(arg.getClass(), ignoreClassArray)) {
                try {
                    String s = OBJECT_MAPPER.writeValueAsString(arg);
                    Object o = OBJECT_MAPPER.readValue(s, arg.getClass());
                    map.put(parameters[i].getName(), o);
                } catch (IOException e) {
                    LOGGER.error("序列化参数出错：" + e.getMessage());
                }
            }
        }
        return map;
    }
}
